1 /**
2 The meta module contains:
3   $(TOC opAAKey)
4   $(TOC GetUniqueConstraintStructNames)
5   $(TOC GetMembersWithUDA)
6   $(TOC hasMembersWithUDA)
7   $(TOC createConstraintStructs)
8   $(TOC GetForeignKeys)
9   $(TOC hasForeignKeys)
10   $(TOC GetForeignKeyRefTable)
11   $(TOC GetDefault)
12   $(TOC hasDefault)
13   $(TOC createForeignKeyPropertyConverter)
14   $(TOC createForeignKeyProperties)
15   $(TOC createForeignKeyCheckExceptions)
16   $(TOC createForeignKeyChanged)
17   $(TOC hasExclusionConstraints)
18   $(TOC GetExclusionConstraints)
19 
20 License: $(GPL2)
21 
22 Authors: Matthew Armbruster
23 
24 $(B Source:) $(SRC $(SRCFILENAME))
25 
26 Copyright: 2016
27  */
28 module db_constraints.utils.meta;
29 
30 import std.meta : NoDuplicates, AliasSeq;
31 import std.traits : isInstanceOf, hasUDA;
32 
33 import db_constraints.constraints;
34 /**
35 Used in KeyedItem for the generated structs.
36 This allows the struct to be used as a key
37 in an associative array.
38 
39 The template loops over the members to define
40 toHash, opEquals, and opCmp for the struct.
41  */
42 mixin template opAAKey(T)
43     if (is(T == struct))
44 {
45     // Gets the hash code of the struct by looping over the members.
46     final size_t toHash() const nothrow @safe
47     {
48         size_t result;
49         foreach(i, dummy; this.tupleof)
50         {
51             if (i == 0)
52             {
53                 result = typeid(this.tupleof[i]).getHash(&this.tupleof[i]);
54             }
55             else
56             {
57                 result ^= typeid(this.tupleof[i]).getHash(&this.tupleof[i]);
58             }
59         }
60         return result;
61     }
62     // Checks each member to determine if the structs are equal.
63     final bool opEquals(inout(T) pk) const pure nothrow @nogc @safe
64     {
65         bool result;
66         foreach(i, dummy; pk.tupleof)
67         {
68             if (this.tupleof[i] == pk.tupleof[i])
69             {
70                 result = true;
71                 continue;
72             }
73             else if (this.tupleof[i] != pk.tupleof[i])
74             {
75                 result = false;
76                 break;
77             }
78             assert(0);
79         }
80         return result;
81     }
82     // Compares each member and returns the result.
83     final int opCmp(inout(T) pk) const pure nothrow @nogc @safe
84     {
85         int result;
86         foreach(i, dummy; pk.tupleof)
87         {
88             if (this.tupleof[i] > pk.tupleof[i])
89             {
90                 result = 1;
91                 break;
92             }
93             else if (this.tupleof[i] < pk.tupleof[i])
94             {
95                 result = -1;
96                 break;
97             }
98             else if (this.tupleof[i] == pk.tupleof[i])
99             {
100                 result = 0;
101                 continue;
102             }
103             assert(0);
104         }
105         return result;
106     }
107 }
108 
109 /**
110 Gets the names given to the different UniqueConstraints for ClassName.
111 The UniqueConstraintColumns are usually put on getters and setters.
112 Returns:
113     AliasSeq of all the distinct UniqueConstraintColumn.name in ClassName
114  */
115 template GetUniqueConstraintStructNames(ClassName)
116 {
117     template Impl(T...)
118     {
119         static if (T.length == 0)
120         {
121             alias Impl = AliasSeq!();
122         }
123         else
124         {
125             static if (__traits(compiles, __traits(getMember, ClassName, T[0])))
126             {
127                 alias Impl = AliasSeq!(Overloads!(__traits(getOverloads, ClassName, T[0])), Impl!(T[1 .. $]));
128             }
129             else
130             {
131                 alias Impl = AliasSeq!(Impl!(T[1 .. $]));
132             }
133         }
134     }
135     template Overloads(S...)
136     {
137         static if (S.length == 0)
138         {
139             alias Overloads = AliasSeq!();
140         }
141         else
142         {
143             enum attributes = Get!(__traits(getAttributes, S[0]));
144             static if (attributes == "")
145             {
146                 alias Overloads = AliasSeq!(Overloads!(S[1 .. $]));
147             }
148             else
149             {
150                 alias Overloads = AliasSeq!(attributes, Overloads!(S[1 .. $]));
151             }
152         }
153     }
154     template Get(P...)
155     {
156         static if (P.length == 0)
157         {
158             enum Get = "";
159         }
160         else
161         {
162             static if (isInstanceOf!(UniqueConstraintColumn, P[0]))
163             {
164                 alias Get = P[0].name;
165             }
166             else
167             {
168                 alias Get = Get!(P[1 .. $]);
169             }
170         }
171     }
172     alias GetUniqueConstraintStructNames = NoDuplicates!(Impl!(__traits(derivedMembers, ClassName)));
173 }
174 
175 /**
176 Gets the properties of ClassName marked with @attribute. If the
177 attribute is PrimaryKeyColumn then it also confirms the property has
178 NotNull.
179 Returns:
180     AliasSeq with distinct properties that have @attribute assigned to it.
181  */
182 template GetMembersWithUDA(ClassName, attribute)
183 {
184     template Impl(T...)
185     {
186         static if (T.length == 0)
187         {
188             alias Impl = AliasSeq!();
189         }
190         else
191         {
192             static if (__traits(compiles, __traits(getMember, ClassName, T[0])) &&
193                        Overloads!(__traits(getOverloads, ClassName, T[0])))
194             {
195                 alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $]));
196             }
197             else
198             {
199                 alias Impl = AliasSeq!(Impl!(T[1 .. $]));
200             }
201         }
202     }
203     template Overloads(P...)
204     {
205         static if (P.length == 0)
206         {
207             enum Overloads = false;
208         }
209         else static if (hasUDA!(P[0], attribute))
210         {
211             static if (__traits(isSame, attribute, PrimaryKeyColumn))
212             {
213                 static assert(hasUDA!(P[0], NotNull),
214                               "Primary key columns must have the NotNull" ~
215                               " attribute which is missing from the class " ~
216                               ClassName.stringof);
217             }
218             enum Overloads = true;
219         }
220         else
221         {
222             alias Overloads = Overloads!(P[1 .. $]);
223         }
224     }
225 
226     alias GetMembersWithUDA = NoDuplicates!(Impl!(__traits(derivedMembers, ClassName)));
227 }
228 
229 /**
230 Confirms there are members in ClassName with @attribute.
231 Returns:
232     true if there are members that have @attribute
233  */
234 template hasMembersWithUDA(ClassName, attribute)
235 {
236     template Impl(T...)
237     {
238         static if (T.length == 0)
239         {
240             enum Impl = false;
241         }
242         else
243         {
244             static if (__traits(compiles, __traits(getMember, ClassName, T[0])) &&
245                        Overloads!(__traits(getOverloads, ClassName, T[0])))
246             {
247                 enum Impl = true;
248             }
249             else
250             {
251                 alias Impl = Impl!(T[1 .. $]);
252             }
253         }
254     }
255     template Overloads(P...)
256     {
257         static if (P.length == 0)
258         {
259             enum Overloads = false;
260         }
261         else static if (hasUDA!(P[0], attribute))
262         {
263             enum Overloads = true;
264         }
265         else
266         {
267             alias Overloads = Overloads!(P[1 .. $]);
268         }
269     }
270 
271     enum hasMembersWithUDA = Impl!(__traits(derivedMembers, ClassName));
272 }
273 
274 /*
275 Using the ClassName and ClusteredIndexAttributeName, createConstraintStructs will
276 append together strings using $(SRCTAG GetUniqueConstraintStructNames) and $(SRCTAG GetMembersWithUDA)
277 that make up the structs used as your unique keys.
278 Returns:
279     A string full of the structs for ClassName that make each row unique.
280  */
281 template createConstraintStructs(ClassName, string ClusteredIndexAttributeName)
282 {
283     string createConstraintStructs()
284     {
285         string result = "public:\n";
286         foreach(name; GetUniqueConstraintStructNames!(ClassName))
287         {
288             static if (name == ClusteredIndexAttributeName)
289             {
290                 result ~= "    final alias " ~ name ~ " = ClusteredIndex;\n";
291                 result ~= "    final alias " ~ name ~ "_key = key;\n";
292             }
293             else
294             {
295                 result ~= "    final struct " ~ name ~ "\n";
296                 result ~= "    {\n";
297                 foreach(columnName; GetMembersWithUDA!(ClassName, UniqueConstraintColumn!name))
298                 {
299                     result ~= "        typeof(" ~ ClassName.stringof ~ "._" ~ columnName ~ ") " ~ columnName ~ ";\n";
300                 }
301                 result ~= "        mixin opAAKey!(" ~ name ~ ");\n";
302                 result ~= "    }\n";
303                 result ~= "    final @property " ~ name ~ " " ~ name ~ "_key() const nothrow pure @safe @nogc\n";
304                 result ~= "    {\n";
305                 result ~= "        auto _" ~ name ~ "_key = " ~ name ~ "();\n";
306                 foreach(columnName; GetMembersWithUDA!(ClassName, UniqueConstraintColumn!name))
307                 {
308                     result ~= "        _" ~ name ~ "_key." ~ columnName ~ " = this._" ~ columnName ~ ";\n";
309                 }
310                 result ~= "        return _" ~ name ~ "_key;\n";
311                 result ~= "    }\n";
312             }
313         }
314         return result;
315     }
316 }
317 
318 /**
319 Gets all of the $(WIKI constraints, ForeignKey) that ClassName is attributed with. If
320 the foreign key name is left blank then the default name is $(D "fk_" ~ ClassName ~ "__" ~ referencedClassName).
321  */
322 template GetForeignKeys(ClassName)
323 {
324     template Impl(T...)
325     {
326         static if (T.length == 0)
327         {
328             alias Impl = AliasSeq!();
329         }
330         else static if (isInstanceOf!(ForeignKey, T[0]))
331         {
332             static if (T[0].name == "")
333             {
334                 enum name = "fk_" ~ ClassName.stringof ~ "_" ~ T[0].referencedTableName;
335                 alias R = ForeignKey!(name, T[0].columnNames, T[0].referencedTableName, T[0].referencedColumnNames, T[0].updateRule, T[0].deleteRule);
336                 alias Impl = AliasSeq!(R, Impl!(T[1 .. $]));
337             }
338             else
339             {
340                 alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $]));
341             }
342         }
343         else
344         {
345             alias Impl = Impl!(T[1 .. $]);
346         }
347     }
348     alias GetForeignKeys = Impl!(__traits(getAttributes, ClassName));
349 }
350 
351 /**
352 Confirms ClassName has foreign keys.
353 Returns:
354     true if ClassName has an instance of @ForeignKey
355  */
356 template hasForeignKeys(ClassName)
357 {
358     template Impl(T...)
359     {
360         static if (T.length == 0)
361         {
362             enum Impl = false;
363         }
364         else static if (isInstanceOf!(ForeignKey, T[0]))
365         {
366             enum Impl = true;
367         }
368         else
369         {
370             alias Impl = Impl!(T[1 .. $]);
371         }
372     }
373     enum hasForeignKeys = Impl!(__traits(getAttributes, ClassName));
374 }
375 
376 /**
377 Gets all of the referenced foreign keys for ClassName.
378 Returns:
379    Distinct list of all referenced classes for ClassName.
380  */
381 template GetForeignKeyRefTable(ClassName)
382     if (hasForeignKeys!(ClassName))
383 {
384     template Impl(T...)
385     {
386         static if (T.length == 0)
387         {
388             alias Impl = AliasSeq!();
389         }
390         else static if (isInstanceOf!(ForeignKey, T[0]))
391         {
392             enum attributes = T[0].referencedTableName;
393             alias Impl = AliasSeq!(attributes, Impl!(T[1 .. $]));
394         }
395         else
396         {
397             alias Impl = Impl!(T[1 .. $]);
398         }
399     }
400     alias GetForeignKeyRefTable = NoDuplicates!(Impl!(__traits(getAttributes, ClassName)));
401 }
402 
403 /**
404 Gets the value for ClassName.memberName inside of @Default!(value) if memberName has @Default!(value)
405  */
406 template GetDefault(ClassName, string memberName)
407     if (hasDefault!(ClassName, memberName))
408 {
409     static if (__traits(compiles, __traits(getMember, ClassName, memberName)))
410     {
411         alias GetDefault = Overloads!(__traits(getOverloads, ClassName, memberName));
412     }
413     else
414     {
415         alias GetDefault = AliasSeq!();
416     }
417     template Overloads(S...)
418     {
419         import std.conv : to;
420         static if (S.length == 0)
421         {
422             alias Overloads = AliasSeq!();
423         }
424         else
425         {
426             enum attributes = Get!(__traits(getAttributes, S[0]));
427             static if (attributes.to!string == "")
428             {
429                 alias Overloads = Overloads!(S[1 .. $]);
430             }
431             else
432             {
433                 alias Overloads = attributes;
434             }
435         }
436     }
437     template Get(P...)
438     {
439         static if (P.length == 0)
440         {
441             enum string Get = "";
442         }
443         else static if (isInstanceOf!(Default, P[0]))
444         {
445             enum Get = P[0].value;
446         }
447         else
448         {
449             alias Get = Get!(P[1 .. $]);
450         }
451     }
452 }
453 
454 /**
455 Confirms ClassName.memberName has @Default!(value)
456 Returns:
457     true if ClassName.memberName has @Default!(value)
458  */
459 template hasDefault(ClassName, string memberName)
460 {
461     static if (__traits(compiles, __traits(getMember, ClassName, memberName)))
462     {
463         enum hasDefault = Overloads!(__traits(getOverloads, ClassName, memberName));
464     }
465     else
466     {
467         enum hasDefault = false;
468     }
469     template Overloads(S...)
470     {
471         static if (S.length == 0)
472         {
473             enum Overloads = false;
474         }
475         else
476         {
477             enum attributes = Get!(__traits(getAttributes, S[0]));
478             static if (attributes)
479             {
480                 enum Overloads = true;
481             }
482             else
483             {
484                 alias Overloads = Overloads!(S[1 .. $]);
485             }
486         }
487     }
488     template Get(P...)
489     {
490         static if (P.length == 0)
491         {
492             enum Get = false;
493         }
494         else static if (isInstanceOf!(Default, P[0]))
495         {
496             enum Get = true;
497         }
498         else
499         {
500             alias Get = Get!(P[1 .. $]);
501         }
502     }
503 }
504 /**
505 Creates the foreign key properties inside of $(D KeyedItem)
506 that convert the keyed items properties into the necessary
507 foreign key clustered index.
508  */
509 template createForeignKeyPropertyConverter(ClassName)
510 {
511     string createForeignKeyPropertyConverter()
512     {
513         string result = "";
514         foreach(foreignKey; GetForeignKeys!(ClassName))
515         {
516             result ~= "final bool " ~ foreignKey.name ~ "_key(out " ~ foreignKey.referencedTableName ~ ".key_type aKey)\n";
517             result ~= "{\n";
518             result ~= "    bool result;\n";
519             result ~= "    static if (\n";
520             for (int i = 0; i < foreignKey.columnNames.length; ++i)
521             {
522                 if (i > 0)
523                 {
524                     result ~= " &&\n";
525                 }
526                 result ~= "        is(typeof(aKey." ~ foreignKey.referencedColumnNames[i] ~ ") == typeof(this." ~ foreignKey.columnNames[i] ~ "))";
527             }
528             result ~= "\n";
529             result ~= "        )\n";
530             result ~= "    {\n";
531             for (int i = 0; i < foreignKey.columnNames.length; ++i)
532             {
533                 result ~= "        aKey." ~ foreignKey.referencedColumnNames[i] ~ " = this." ~ foreignKey.columnNames[i] ~ ";\n";
534             }
535             result ~= "        result = true;\n";
536             result ~= "    }\n";
537             result ~= "    else static if (__traits(compiles,\n";
538             result ~= "                             (" ~ ClassName.stringof ~ " b)\n";
539             result ~= "                             {\n";
540             foreach(columnName; foreignKey.columnNames)
541             {
542                 result ~= "                                 if (b." ~ columnName ~ ".isNull == true) { }\n";
543             }
544             result ~= "                             }))\n";
545             result ~= "    {\n";
546             result ~= "        if (\n";
547             foreach(columnName; foreignKey.columnNames)
548             {
549                 if (columnName != foreignKey.columnNames[0])
550                 {
551                     result ~= " &&\n";
552                 }
553                 result ~= "            !this." ~ columnName ~ ".isNull";
554             }
555             result ~= "\n";
556             result ~= "           )\n";
557             result ~= "        {\n";
558             for (int i = 0; i < foreignKey.columnNames.length; ++i)
559             {
560                 result ~= "            aKey." ~ foreignKey.referencedColumnNames[i] ~ " = this." ~ foreignKey.columnNames[i] ~ ";\n";
561             }
562             result ~= "            result = true;\n";
563             result ~= "        }\n";
564             result ~= "        else\n";
565             result ~= "        {\n";
566             result ~= "            result = false;\n";
567             result ~= "        }\n";
568             result ~= "    }\n";
569             result ~= "    else\n";
570             result ~= "    {\n";
571             result ~= "        static assert(false, \"Column type mismatch for "~ foreignKey.name ~ ".\");\n";
572             result ~= "    }\n";
573             result ~= "    return result;\n";
574             result ~= "}\n";
575         }
576         return result;
577     }
578 }
579 
580 /**
581 Creates the foreign key properties that will be used in KeyedCollection.
582 It loops over all of the foreign key attributes for ClassName and creates
583 a write-only property for each referenced class by using the class' name
584 in lower case. There is a static assert that makes sure the lower case
585 class name does not equal the class name. This would result in name
586 collisions and is not conforming to the D style.
587 
588 There are two setters that are made. One takes the referenced class
589 by reference and the other accepts null. The null setter removes the
590 reference and disconnects the emitted signals. The setter that takes
591 the class by reference connects the emitted signals and keeps the
592 address of the class to check foreign key constraints when anything
593 changes.
594  */
595 template createForeignKeyProperties(ClassName)
596 {
597     string createForeignKeyProperties()
598     {
599         import std.uni : toLower;
600         string result = "";
601         foreach(member; GetForeignKeyRefTable!(ClassName))
602         {
603             static assert(member != member.toLower, "The class " ~ member ~ " should start with a capital letter to use Foreign Keys or else there will be name collisions.");
604             result ~= "private " ~ member ~ " *_" ~ member.toLower ~ ";\n";
605             result ~= "private " ~ member ~ ".key_type _changed" ~ member ~ "Row;\n";
606 
607             result ~= "final @property void " ~ member.toLower ~ "(ref " ~ member ~ " " ~ member.toLower ~ "_)\n";
608             result ~= "{\n";
609             result ~= "    this." ~ member.toLower ~ " = null;\n";
610             result ~= "    this._" ~ member.toLower ~ " = &" ~ member.toLower ~ "_;\n";
611             foreach(foreignKey; GetForeignKeys!(ClassName))
612             {
613                 static if (foreignKey.referencedTableName == member)
614                 {
615                     result ~= "    this._" ~ member.toLower ~ ".collectionChanged.connect(&" ~ foreignKey.name ~ "_Changed);\n";
616                 }
617             }
618             result ~= "    checkForeignKeys();\n";
619             result ~= "}\n";
620 
621 
622             result ~= "final @property void " ~ member.toLower ~ "(typeof(null) n)\n";
623             result ~= "{\n";
624             result ~= "    if (this._" ~ member.toLower ~ " !is null)\n";
625             result ~= "    {\n";
626             foreach(foreignKey; GetForeignKeys!(ClassName))
627             {
628                 static if (foreignKey.referencedTableName == member)
629                 {
630                     result ~= "        this._" ~ member.toLower ~ ".collectionChanged.disconnect(&" ~ foreignKey.name ~ "_Changed);\n";
631                 }
632             }
633             result ~= "    this._" ~ member.toLower ~ " = null;\n";
634             result ~= "    }\n";
635             result ~= "}\n";
636         }
637         return result;
638     }
639 }
640 
641 /**
642 Creates the foreign key check exceptions by seeing if the foreign key has been associated and
643 whether or not the referenced table has a matching record.
644  */
645 template createForeignKeyCheckExceptions(ClassName)
646 {
647     string createForeignKeyCheckExceptions()
648     {
649         import std.uni : toLower;
650         string result = "";
651         foreach(foreignKey; GetForeignKeys!(ClassName))
652         {
653             static assert(foreignKey.referencedTableName != foreignKey.referencedTableName.toLower, "The class " ~ member ~ " should start with a capital letter to use Foreign Keys or else there will be name collisions.");
654             result ~= "if (this._" ~ foreignKey.referencedTableName.toLower ~ " !is null)\n";
655             result ~= "{\n";
656             result ~= "    " ~ foreignKey.referencedTableName ~ ".key_type i;\n";
657             result ~= "    if(a." ~ foreignKey.name ~ "_key(i))\n";
658             result ~= "    {\n";
659             result ~= "        enforceEx!ForeignKeyException(this._" ~ foreignKey.referencedTableName.toLower ~ ".contains(i), \"" ~ foreignKey.name ~ " violation.\");\n";
660             result ~= "    }\n";
661             result ~= "}\n";
662         }
663         return result;
664     }
665 }
666 
667 /**
668 Creates the foreign keys update rule and delete rule property and sets them to the foreign key attribute property.
669 It also creates the function that will be attached to the foreign key when it is associated. This is where
670 the update rule and delete rule are used since the referenced class will emit what changed and to which item.
671  */
672 template createForeignKeyChanged(ClassName)
673 {
674     string createForeignKeyChanged()
675     {
676         import std.conv : to;
677         string result = "";
678         foreach(foreignKey; GetForeignKeys!(ClassName))
679         {
680             result ~= "Rule " ~ foreignKey.name ~ "_UpdateRule = Rule." ~ foreignKey.updateRule.to!string ~ ";\n";
681             result ~= "Rule " ~ foreignKey.name ~ "_DeleteRule = Rule." ~ foreignKey.deleteRule.to!string ~ ";\n";
682             result ~= "void " ~ foreignKey.name ~ "_Changed(string propertyName, " ~ foreignKey.referencedTableName ~ ".key_type item_key)\n";
683             result ~= "{\n";
684             result ~= "    if (canFind(" ~ foreignKey.referencedColumnNames.to!string ~ ", propertyName))\n";
685             result ~= "    {\n";
686             result ~= "        this._changed" ~ foreignKey.referencedTableName ~ "Row = item_key;\n";
687             result ~= "    }\n";
688             result ~= "    else if (propertyName == \"key\")\n";
689             result ~= "    {\n";
690             // onUpdate
691             result ~= "        auto changed" ~ foreignKey.referencedTableName ~ " = this.byValue.filter!(\n";
692             result ~= "            (" ~ ClassName.stringof ~ " a) =>\n";
693             result ~= "            {\n";
694             result ~= "                " ~ foreignKey.referencedTableName ~ ".key_type i;\n";
695             result ~= "                return (a." ~ foreignKey.name ~ "_key(i) ? i == this._changed" ~ foreignKey.referencedTableName ~ "Row : false);\n";
696             result ~= "            }());\n";
697             result ~= "        final switch (" ~ foreignKey.name ~ "_UpdateRule) with (Rule)\n";
698             result ~= "        {\n";
699             result ~= "        case noAction:\n";
700             result ~= "            break;\n";
701             result ~= "        case restrict:\n";
702             result ~= "            if (!changed" ~ foreignKey.referencedTableName ~ ".empty)\n";
703             result ~= "                throw new ForeignKeyException(\"" ~ foreignKey.name ~ " violation.\");\n";
704             result ~= "            break;\n";
705             result ~= "        case setNull:\n";
706             result ~= "        static if (__traits(compiles,\n";
707             result ~= "                            (" ~ ClassName.stringof ~ " a)\n";
708             result ~= "                            {\n";
709             foreach(columnName; foreignKey.columnNames)
710             {
711                 result ~= "                                a." ~ columnName ~ " = null;\n";
712             }
713             result ~= "                            }))\n";
714             result ~= "            {\n";
715             result ~= "                changed" ~ foreignKey.referencedTableName ~ ".each!(\n";
716             result ~= "                    (" ~ ClassName.stringof ~ " a) =>\n";
717             result ~= "                    {\n";
718             foreach(columnName; foreignKey.columnNames)
719             {
720                 result ~= "                        a." ~ columnName ~ " = null;\n";
721             }
722             result ~= "                    }());\n";
723             result ~= "                break;\n";
724             result ~= "            }\n";
725             result ~= "            else\n";
726             result ~= "            {\n";
727             result ~= "                throw new ForeignKeyException(\"" ~ foreignKey.name ~ ". Cannot use Rule.setNull when the member cannot be set to null.\");\n";
728             result ~= "            }\n";
729             result ~= "        case setDefault:\n";
730             result ~= "            changed" ~ foreignKey.referencedTableName ~ ".each!(\n";
731             result ~= "                (" ~ ClassName.stringof ~ " a) =>\n";
732             result ~= "                {\n";
733             foreach(columnName; foreignKey.columnNames)
734             {
735                 result ~= "                    static if (hasDefault!(" ~ ClassName.stringof ~ ", \"" ~ columnName ~ "\"))";
736                 result ~= "                    {\n";
737                 result ~= "                        a." ~ columnName ~ " = GetDefault!(" ~ ClassName.stringof ~ ", \"" ~ columnName ~ "\");\n";
738                 result ~= "                    }\n";
739                 result ~= "                    else\n";
740                 result ~= "                    {\n";
741                 result ~= "                        a." ~ columnName ~ " = typeof(a." ~ columnName ~ ").init;\n";
742                 result ~= "                    }\n";
743             }
744             result ~= "                }());\n";
745             result ~= "            break;\n";
746             result ~= "        case cascade:\n";
747             result ~= "            changed" ~ foreignKey.referencedTableName ~ ".each!(\n";
748             result ~= "                (" ~ ClassName.stringof ~ " a) =>\n";
749             result ~= "                {\n";
750             for (int i = 0; i < foreignKey.columnNames.length; ++i)
751             {
752                 result ~= "                    a." ~ foreignKey.columnNames[i] ~ " = item_key." ~ foreignKey.referencedColumnNames[i] ~ ";\n";
753             }
754             result ~= "                }());\n";
755             result ~= "            break;\n";
756 
757             result ~= "        }\n";
758             result ~= "    }\n";
759             result ~= "    else if (propertyName == \"remove\")\n";
760             result ~= "    {\n";
761             // onDelete
762             result ~= "        auto removed" ~ foreignKey.referencedTableName ~ " = this.byValue.filter!(\n";
763             result ~= "            (" ~ ClassName.stringof ~ " a) =>\n";
764             result ~= "            {\n";
765             result ~= "                " ~ foreignKey.referencedTableName ~ ".key_type i;\n";
766             result ~= "                return (a." ~ foreignKey.name ~ "_key(i) ? i == item_key : false);\n";
767             result ~= "            }());\n";
768             result ~= "        final switch (" ~ foreignKey.name ~ "_DeleteRule) with (Rule)\n";
769             result ~= "        {\n";
770             result ~= "        case noAction:\n";
771             result ~= "            break;\n";
772             result ~= "        case restrict:\n";
773             result ~= "            if (!removed" ~ foreignKey.referencedTableName ~ ".empty)\n";
774             result ~= "                throw new ForeignKeyException(\"" ~ foreignKey.name ~ " violation.\");\n";
775             result ~= "            break;\n";
776             result ~= "        case setNull:\n";
777             result ~= "        static if (__traits(compiles,\n";
778             result ~= "                            (" ~ ClassName.stringof ~ " a)\n";
779             result ~= "                            {\n";
780             foreach(columnName; foreignKey.columnNames)
781             {
782                 result ~= "                                a." ~ columnName ~ " = null;\n";
783             }
784             result ~= "                            }))\n";
785             result ~= "            {\n";
786             result ~= "                removed" ~ foreignKey.referencedTableName ~ ".each!(\n";
787             result ~= "                    (" ~ ClassName.stringof ~ " a) =>\n";
788             result ~= "                    {\n";
789             foreach(columnName; foreignKey.columnNames)
790             {
791                 result ~= "                        a." ~ columnName ~ " = null;\n";
792             }
793             result ~= "                    }());\n";
794             result ~= "                break;\n";
795             result ~= "            }\n";
796             result ~= "            else\n";
797             result ~= "            {\n";
798             result ~= "                throw new ForeignKeyException(\"" ~ foreignKey.name ~ ". Cannot use Rule.setNull when the member cannot be set to null.\");\n";
799             result ~= "            }\n";
800             result ~= "        case setDefault:\n";
801             result ~= "            removed" ~ foreignKey.referencedTableName ~ ".each!(\n";
802             result ~= "                (" ~ ClassName.stringof ~ " a) =>\n";
803             result ~= "                {\n";
804             foreach(columnName; foreignKey.columnNames)
805             {
806                 result ~= "                    static if (hasDefault!(" ~ ClassName.stringof ~ ", \"" ~ columnName ~ "\"))";
807                 result ~= "                    {\n";
808                 result ~= "                        a." ~ columnName ~ " = GetDefault!(" ~ ClassName.stringof ~ ", \"" ~ columnName ~ "\");\n";
809                 result ~= "                    }\n";
810                 result ~= "                    else\n";
811                 result ~= "                    {\n";
812                 result ~= "                        a." ~ columnName ~ " = typeof(a." ~ columnName ~ ").init;\n";
813                 result ~= "                    }\n";
814             }
815             result ~= "                }());\n";
816             result ~= "            break;\n";
817 
818             result ~= "        case cascade:\n";
819             result ~= "            removed" ~ foreignKey.referencedTableName ~ ".each!(\n";
820             result ~= "                (" ~ ClassName.stringof ~ " a) =>\n";
821             result ~= "                {\n";
822             result ~= "                    this.remove(a.key);\n";
823             result ~= "                }());\n";
824             result ~= "            break;\n";
825             result ~= "        }\n";
826             result ~= "    }\n";
827             result ~= "}\n";
828         }
829         return result;
830     }
831 }
832 
833 /**
834 Confirms ClassName has exclusion constraints.
835 Returns:
836     true if ClassName has an instance of @ExclusionConstraint
837  */
838 template hasExclusionConstraints(ClassName)
839 {
840     template Impl(T...)
841     {
842         static if (T.length == 0)
843         {
844             enum Impl = false;
845         }
846         else static if (isInstanceOf!(ExclusionConstraint, T[0]))
847         {
848             enum Impl = true;
849         }
850         else
851         {
852             alias Impl = Impl!(T[1 .. $]);
853         }
854     }
855     enum hasExclusionConstraints = Impl!(__traits(getAttributes, ClassName));
856 }
857 
858 /**
859 Gets all of the $(WIKI constraints, ExclusionConstraint) that ClassName is attributed with. If
860 the exclusion constraint name is left blank then the default name is $(D "exc_" ~ ClassName).
861  */
862 template GetExclusionConstraints(ClassName)
863 {
864     template Impl(T...)
865     {
866         static if (T.length == 0)
867         {
868             alias Impl = AliasSeq!();
869         }
870         else static if (isInstanceOf!(ExclusionConstraint, T[0]))
871         {
872             static if (T[0].name == "")
873             {
874                 enum name = "exc_" ~ ClassName.stringof;
875                 alias R = ExclusionConstraint!(T[0].exclusion, name);
876                 alias Impl = AliasSeq!(R, Impl!(T[1 .. $]));
877             }
878             else
879             {
880                 alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $]));
881             }
882         }
883         else
884         {
885             alias Impl = Impl!(T[1 .. $]);
886         }
887     }
888     alias GetExclusionConstraints = Impl!(__traits(getAttributes, ClassName));
889 }